#ifndef __M_FILER_H__
#define __M_FILER_H__
#include <iostream>
#include <string>
#include <fstream>
#include <string.h>
#include<cerrno>
#include <sys/stat.h>

#include "logger.hpp"

/*
    实现一个文件操作类
*/

class Filer
{
public:
    // 创建文件
    static bool createFile(const std::string &filename)
    {
        std::fstream fs(filename, std::ios::binary | std::ios::out);
        if (fs.is_open() == false)
        {
            ERR_LOG("文件[%s]打开失败", filename.c_str());
            return false;
        }
        fs.close();
        return true;
    }
    // 删除文件
    static bool removeFile(const std::string &filename)
    {
        return (std::remove(filename.c_str()) == 0);
    }
    // 创建目录
    static bool createDirectory(const std::string &path)
    {
        // 例子: a/bb/ccc/dddd
        size_t pos = 0, index = 0;
        std::string sub_path;
        while (index < path.size())
        {
            pos = path.find("/", index);
            if (pos == std::string::npos)
            {
                // sub_path = path.substr(index);
                // return (mkdir(sub_path.c_str(), 0775) == 0);
                return (mkdir(path.c_str(), 0775) == 0);    // 注意此时创建应当对整体进行创建而不是截取后创建
            }
            sub_path = path.substr(0, pos); // 注意这里传入为0，因为我们这个模块，并没有cd到下一层
            if (mkdir(sub_path.c_str(), 0775) != 0 && errno != EEXIST)  // 创建目录失败，文件存在导致的失败
            {
                ERR_LOG("创建目录[%s]失败, 错误为[%s]", sub_path.c_str(), strerror(errno));
                return false;
            }
            index = pos + 1;
        }
        return true;
    }
    // 删除目录
    static bool removeDirectory(const std::string &path)
    {
        // rm -f
        std::string cmd = "rm -rf " + path;
        return (system(cmd.c_str()) != -1);
    }
    // 获取父级目录
    static std::string parentDirectory(const std::string &filename)
    {
        size_t pos = filename.rfind("/");
        if (pos == std::string::npos)
        {
            return "./"; // 表示没有父级目录
        }
        std::string path = filename.substr(0, pos);
        return path;
    }

public:
    Filer(const std::string &filename) : _filename(filename) {}
    // 文件重命名
    bool rename(const std::string &name)
    {
        return std::rename(_filename.c_str(), name.c_str());
    }
    // 文件是否存在
    bool exist()
    {
        struct stat st;
        return stat(_filename.c_str(), &st) == 0; // 返回值为0，表示文件存在
    }
    // 文件大小
    size_t size()
    {
        struct stat st;
        if (stat(_filename.c_str(), &st) != 0)
            return 0;
        else
            return st.st_size;
    }
    // 文件读写
    bool read(char *body, size_t offset, size_t length) // 从文件的offset位置读取length长度的内容
    {
        // 1.打开文件
        std::ifstream ifs(_filename.c_str(), std::ios::binary | std::ios::in);
        if (ifs.is_open() == false)
        {
            ERR_LOG("文件[%s]打开失败", _filename.c_str());
            return false;
        }
        // 2.跳转文件读取位置
        ifs.seekg(offset, std::ios::beg);
        // 3.读取文件数据
        ifs.read(body, length);
        if (ifs.good() == false)
        {
            ERR_LOG("文件[%s]读取失败, 错误[%s]", _filename.c_str(), strerror(errno));
            ifs.close();
            return false;
        }
        // 4.关闭文件
        ifs.close();
        return true;
    }
    bool read(std::string &body)
    {
        body.resize(this->size());  // 需要调整空间大小
        return this->read(&body[0], 0, this->size());
    }
    bool write(const char *body, size_t offset, size_t length) // 从文件的offset位置写入length长度的内容
    {
        // 1.打开文件
        std::fstream fs(_filename.c_str(), std::ios::binary | std::ios::in | std::ios::out);
        if (fs.is_open() == false)
        {
            ERR_LOG("文件[%s]打开失败", _filename.c_str());
            return false;
        }
        // 2.跳转文件读取位置
        fs.seekp(offset, std::ios::beg);
        // 3.写入文件数据
        fs.write(body, length);
        if (fs.good() == false)
        {
            ERR_LOG("文件[%s]写入失败", _filename.c_str());
            fs.close();
            return false;
        }
        // 4.关闭文件;
        fs.close();
        return true;
    }
    bool write(const std::string &body)
    {
        return this->write(body.c_str(), 0, body.size());
    }

private:
    std::string _filename;
};
#endif
